extends layout

block headContent
	title Block Stats

block content
	+pageTitle("Block Stats")

	if (rpcApiUnsupportedError)
		div.alert.alert-danger(style="margin-bottom: 200px;")
			span Your node version doesn't support the 
				span #{rpcApiUnsupportedError.rpc}
				span  RPC. To use this tool you need to upgrade to 
				span v#{rpcApiUnsupportedError.version}
				span  or later.

	else

		+dismissableInfoAlert("blockStatsPageNoteDismissed", "About Block Stats...")
			| !{markdown("The **Block Stats** tool queries a range of blocks from Bitcoin Core and summarizes the data via graphs. These graphs sometimes expose interesting trends and sometimes show boring, steady-state operation of the network.")}
			| Use the <b>Filters</b> section below to select the range of blocks to analyze and select the subset of graphs displayed.

				


		- var graphIds = ["fee-rates", "max-fee-rates", "total-fee", "min-fees", "max-fees", "inputs-outputs", "tx-sizes", "max-tx-sizes", "volumes", "weights-sizes", "sw-txs", "subsidy", "mediantime", "txs", "utxo_increase"];
	
		- var graphTitles = ["Fee Rates (avg, median, min)", "Maximum Fee Rate", "Total Fees", "Minimum Tx Fee", "Maximum Tx Fee", "Input / Output Count", "Tx Sizes (avg, median, min)", "Maximum Tx Size", "Output Volume", "Block Weight and Size", "SegWit Tx Count", "Subsidy", "Timestamp", "Tx Count", "UTXO Δ"];

		+contentSection("Filters")
			.mb-3
				+blockRangeFilters

			hr

			div.mb-2
				span.border-dotted.text-card-highlight.text-uppercase.fw-light(title="Select the list of graphs to be displayed.", data-bs-toggle="tooltip") Active Graphs

			.clearfix
				each graphId, graphIndex in graphIds
					div.float-start.me-3.mb-1
						div.form-check
							input.form-check-input.graph-toggle-checkbox(type="checkbox", id=`checkbox-${graphId}` checked="checked", data-graphId=graphId, autocomplete="off")
							label.form-check-label(for=("checkbox-" + graphId)) #{graphTitles[graphIndex]}

				.float-start.me-3.mb-1
					a.me-3(id="link-show-all", href="javascript:void(0)") show all
					a(id="link-hide-all", href="javascript:void(0)") hide all
				

		div#progress-wrapper.mb-huge
			div.card.shadow-sm.mb-3
				div.card-body
					span Loading blocks: 
						span(id="block-progress-text")
					div.progress.mt-2(id="progress-bar", style="height: 7px;")
						div.progress-bar(id="data-progress", role="progressbar", aria-valuenow="0", aria-valuemin="0" ,aria-valuemax="100")
	

	

	div(id="main-content", style="display: none;")
		.row.clearfix
			each graphId, graphIndex in graphIds
				.col-lg-6.float-start
					.graph-wrapper(id=`graph-wrapper-${graphId}`)
						+sectionTitle(graphTitles[graphIndex])
						.card.shadow-sm.mb-3
							.card-body
								canvas.mb-3(id=graphId)


block endOfBody

	+graphPageScriptSetup
	

	script.
		var currentBlockHeight = !{currentBlockHeight};
		$(document).ready(function() {
			$("#time-range-buttons .block-count-btn").on("click", function() {
				// highlight current selection
				$("#time-range-buttons .block-count-btn").removeClass("btn-primary").addClass("btn-outline-primary");
				$(this).addClass("btn-primary").removeClass("btn-outline-primary");
				$("#preconfigured-dropdown").removeClass("btn-primary").addClass("btn-outline-primary");
				
				var blockCount = parseInt($(this).attr("data-blockCount"));

				$("#data-progress").css("width", "0%");
				$("#block-progress-text").text("");

				$("#main-content").hide();
				$("#progress-wrapper").show();

				getData(currentBlockHeight, blockCount, 15);
			});

			$("#block-selections-buttons .dropdown-item").on("click", function() {
				// highlight current selection
				$("#time-range-buttons .block-count-btn").removeClass("btn-primary").addClass("btn-outline-primary");
				$("#preconfigured-dropdown").removeClass("btn-outline-primary").addClass("btn-primary");
				
				var blocks = $(this).attr("data-blocks");
				var bStartEnd = blocks.split("-");
				var bStart = parseInt(bStartEnd[0]);
				var bEnd = parseInt(bStartEnd[1]);

				$("#data-progress").css("width", "0%");
				$("#block-progress-text").text("");

				$("#main-content").hide();
				$("#progress-wrapper").show();

				getData(bEnd, bEnd - bStart + 1, 15);
			});

			$("#custom-range-form").on("submit", function() {
				// highlight current selection
				$("#time-range-buttons .block-count-btn").removeClass("btn-primary").addClass("btn-outline-primary");
				$("#preconfigured-dropdown").removeClass("btn-primary").addClass("btn-outline-primary");
				
				var bStart = parseInt($("#custom-range-start").val());
				var bEnd = parseInt($("#custom-range-end").val());

				$("#data-progress").css("width", "0%");
				$("#block-progress-text").text("");

				$("#main-content").hide();
				$("#progress-wrapper").show();

				getData(bEnd, bEnd - bStart + 1, 15);

				return false;
			});

			$(".graph-toggle-checkbox").change(function() {
				var graphId = $(this).attr("data-graphId");

				$("#graph-wrapper-" + graphId).toggle();
			});

			$("#link-show-all").on("click", function() {
				$(".graph-toggle-checkbox").each(function() {
					$(this).prop("checked", true);
				});

				$(".graph-wrapper").each(function() {
					$(this).show();
				});
			});

			$("#link-hide-all").on("click", function() {
				$(".graph-toggle-checkbox").each(function() {
					$(this).prop("checked", false);
				});

				$(".graph-wrapper").each(function() {
					$(this).hide();
				});
			});
		});

		function getData(blockStart, count, chunkSize) {
			$("#time-range-buttons .block-count-btn").addClass("disabled");
			$("#block-selections-buttons .dropdown-item").addClass("disabled");

			var chunks = [];
			var blockIndex = blockStart;
			while (blockIndex > blockStart - count) {
				var chunk = [];
				for (var i = blockIndex; (i > (blockIndex - chunkSize) && i > (blockStart - count)); i--) {
					chunk.push(i);
				}

				blockIndex -= chunkSize;
				chunks.push(chunk);
			}

			console.log(JSON.stringify(chunks));
			//alert(JSON.stringify(chunks));

			var results = [];

			var statusCallback = function(chunkIndexDone, chunkCount) {
				//console.log("Done: " + Math.min(((chunkIndexDone + 1) * chunkSize), count) + " of " + count);

				var wPercent = `${parseInt(100 * (chunkIndexDone + 1) / parseFloat(chunkCount))}%`;
				
				$("#data-progress").css("width", wPercent);
				$("#block-progress-text").text(`${Math.min(((chunkIndexDone + 1) * chunkSize), count).toLocaleString()} of ${count.toLocaleString()} (${wPercent})`);
			};

			var finishedCallback = function() {
				$("#time-range-buttons .block-count-btn").removeClass("disabled");
				$("#block-selections-buttons .dropdown-item").removeClass("disabled");

				var summary = summarizeData(results);

				var red = "#dc3545";
				var green = "#28a745";
				var blue = "#007bff";

				createGraph("fee-rates", ["avg-fee-rate", "median-fee-rate", "min-fee-rate"], [summary.avgfeerate, summary.medianfeerate, summary.minfeerate], [blue, red, green]);
				
				createGraph("max-fee-rates", ["max-fee-rate"], [summary.maxfeerate], [blue]);

				createGraph("total-fee", ["total-fee"], [summary.totalfee], [blue]);

				createGraph("min-fees", ["min-fee"], [summary.minfee], [blue]);
				createGraph("max-fees", ["max-fee"], [summary.maxfee], [blue]);

				createGraph("inputs-outputs", ["inputs", "outputs"], [summary.inputs, summary.outputs], [blue, green]);

				createGraph("tx-sizes", ["avg-tx-size", "median-tx-size", "min-tx-size"], [summary.avgtxsize, summary.mediantxsize, summary.mintxsize], [blue, red, green]);

				createGraph("max-tx-sizes", ["max-tx-size"], [summary.maxtxsize], [blue]);

				createGraph("volumes", ["volume"], [summary.totaloutput], [blue]);

				createGraph("weights-sizes", ["weight", "size"], [summary.weight, summary.size], [blue, green]);

				createGraph("sw-txs", ["swtxs"], [summary.swtxs], [blue]);

				createGraph("subsidy", ["subsidy"], [summary.subsidy], [blue], false);

				createGraph("mediantime", ["mediantime"], [summary.mediantime], [blue], false);

				createGraph("txs", ["txs"], [summary.txs], [blue]);

				createGraph("utxo_increase", ["utxo_increase"], [summary.utxo_increase], [blue], false);

				


				$("#main-content").show();
				$("#progress-wrapper").hide();
			};

			getBlockData(results, chunks, 0, statusCallback, finishedCallback);
		}

		function getBlockData(results, chunks, chunkIndex, statusCallback, finishedCallback) {
			if (chunkIndex > chunks.length - 1) {
				finishedCallback();

				return;
			}

			var url = `./internal-api/block-stats-by-height/${chunks[chunkIndex]}`;
			
			//console.log(url);

			$.ajax({
				url: url

			}).done(function(result) {
				for (var i = 0; i < result.length; i++) {
					results.push(result[i]);
				}

				statusCallback(chunkIndex, chunks.length);
				
				getBlockData(results, chunks, chunkIndex + 1, statusCallback, finishedCallback);
			});
		}

		function summarizeData(results) {
			var summary = {};

			summary.avgfeerate = [];
			summary.medianfeerate = [];
			summary.minfeerate = [];

			summary.maxfeerate = [];

			summary.totalfee = [];

			summary.minfee = [];
			summary.maxfee = [];

			summary.inputs = [];
			summary.outputs = [];

			summary.avgtxsize = [];
			summary.mediantxsize = [];
			summary.mintxsize = [];

			summary.maxtxsize = [];
			
			summary.totaloutput = [];

			summary.weight = [];
			summary.size = [];

			summary.swtxs = [];

			summary.subsidy = [];

			summary.mediantime = [];

			summary.txs = [];

			summary.utxo_increase = [];

			for (var i = results.length - 1; i >= 0; i--) {
				summary.avgfeerate.push({x:results[i].height, y:results[i].avgfeerate});
				summary.medianfeerate.push({x:results[i].height, y:results[i].feerate_percentiles[2]});
				summary.minfeerate.push({x:results[i].height, y:results[i].minfeerate});
				
				summary.maxfeerate.push({x:results[i].height, y:results[i].maxfeerate});

				summary.totalfee.push({x:results[i].height, y:new Decimal(results[i].totalfee).dividedBy(100000000)});
				
				summary.minfee.push({x:results[i].height, y:results[i].minfee});
				summary.maxfee.push({x:results[i].height, y:results[i].maxfee});
				
				summary.inputs.push({x:results[i].height, y:results[i].ins});
				summary.outputs.push({x:results[i].height, y:results[i].outs});
				
				summary.avgtxsize.push({x:results[i].height, y:results[i].avgtxsize});
				summary.mediantxsize.push({x:results[i].height, y:results[i].mediantxsize});
				summary.mintxsize.push({x:results[i].height, y:results[i].mintxsize});

				summary.maxtxsize.push({x:results[i].height, y:results[i].maxtxsize});
				
				summary.totaloutput.push({x:results[i].height, y:new Decimal(results[i].total_out).dividedBy(100000000)});
				
				summary.weight.push({x:results[i].height, y:results[i].total_weight});
				summary.size.push({x:results[i].height, y:results[i].total_size});
				
				summary.swtxs.push({x:results[i].height, y:results[i].swtxs});

				summary.subsidy.push({x:results[i].height, y:results[i].subsidy});

				summary.mediantime.push({x:results[i].height, y:results[i].mediantime});

				summary.txs.push({x:results[i].height, y:results[i].txs});

				summary.utxo_increase.push({x:results[i].height, y:results[i].utxo_increase});
			}

			return summary;
		}

		var graphsById = {};
		function createGraph(chartid, names, datas, colors, logYAxis=true) {
			var datasets = [];
			var yaxes = [];

			for (var i = 0; i < names.length; i++) {
				datasets.push({
					label: names[i],
					data: datas[i],
					borderWidth: 2,
					borderColor: colors[i],
					backgroundColor: 'rgba(0, 0, 0, 0)',
					pointRadius: 1,
					borderJoinStyle: "round"
				});

				yaxes.push({
					scaleLabel: {
						display: false,
						//labelString: names[i]
					},
					grid: {
						color: gridLineColor
					},
				});
			}

			// update data in graph if we already created, otherwise create anew
			if (graphsById[chartid]) {
				graph = graphsById[chartid];
				graph.data = { datasets: datasets };
				graph.update();

			} else {
				var ctx = document.getElementById(chartid).getContext('2d');
				var graph = new Chart(ctx, {
					type: 'line',
					data: {
						datasets: datasets
					},
					options: {
						// disable all animations
						animation: false,

						interaction: {
							intersect: false,
							mode: 'index',
						},

						plugins: {
							legend: {
								display: (datasets.length > 1)
							},
						},
						scales: {
							x: {
								type: 'linear',
								position: 'bottom',
								scaleLabel: {
									display: true,
									labelString: 'Block'
								},
								grid: {
									color: gridLineColor
								},
							},
							y: {
								type: logYAxis ? "logarithmic" : "linear",
								position: 'left',
								scaleLabel: {
									display: false,
								},
								grid: {
									color: gridLineColor
								},
							},
						}
					}
				});

				graphsById[chartid] = graph;
			}
		}

	if (rpcApiUnsupportedError == null)
		script.
			$(document).ready(function() {
				getData(currentBlockHeight, 144, 15);
			});
		
